#!coding:utf-8
import random
import uuid
from celery import current_app
from django.db import transaction
from cvirt.common import exception
from cvirt.common import config
from cvirt.manager.models import Server
import logging
# import inspect
# from cvirt.manager.models import ScheduleTestTable
from cvirt.manager.models import Instance, Snapshot
from cvirt.cnode.vm import power_state

"""
    task调用规则如下：
    1.参数可以使用model，celery使用pickle序列化参数
    2.原则上作为参数的model可以不是db bind的对象，序列化后的对象也是可用执行插入操作的
"""
config.setup_logging()

LOG = logging.getLogger("tasks")

conf = config.load_settings()

class pingMixin(object):
    def ping(self, *hosts):
        if hosts:
            hosts =  current_app.control.ping(hosts, timeout=conf.get_float("inspect.ping.timeout"))
        else:
            hosts = current_app.control.ping(timeout=conf.get_float("inspect.ping.timeout"))
        return [h.keys()[0] for h in hosts]

    def service_is_alive(self, host):
        return bool(self.ping(host))

    def pick_host(self,hosts):
        #自动选择host逻辑，默认是随机选择
        if hosts:
            return hosts[random.randint(0,len(hosts)-1)]

class commitMixin(object):
    def _commit_db(self):
        try:
            transaction.commit()
        except:
            pass

class TaskWrap(pingMixin,commitMixin):
    '''
    包装task类，用于广播，异步和同步调用，以及探测service是否alive
    '''

    def __init__(self, task):
        self.task = task

    def boadcast(self,*args, **kwargs):
        self._commit_db()
        result = self.task.apply_async(args=args, kwargs=kwargs, queue='all')
        return result

    def async_call(self, host=None, *args, **kwargs):
        "args,kwargs 是task function的参数,调用task时直接传递"
        if host is None:
            host = self.pick_host(self.ping())
        if host is None or not self.service_is_alive(host):
            raise exception.ServiceNotFound(host,topic=host)
        #在调用task之前commit db，防止task读取脏数据的问题

        self._commit_db()

        result = self.task.apply_async(queue=host, args=args, kwargs=kwargs)
        return result

    def alive_hosts(self,cluster_id):
        #这里不能只拿status=2的host，应该获取未激活和维护模式之外的所有的host
        link_q = [s.host_name for s in Server.objects.exclude(status__in=[0,1]).filter(cluster__id=cluster_id).all()]
        if link_q:
            #这里我们只需要活着的节点
            return self.ping(*link_q)

    def cluster_call(self, cluster_id, *args, **kwargs):
        if not isinstance(self.task,link_sync_call):
            raise exception.TaskShouldBeLinkSyncCall()

        alive = self.alive_hosts(cluster_id)
        LOG.info('send cluster_call to %s' % alive)
        if not alive:
            raise exception.ServiceNotFound(message="no host is alive")

        first = alive.pop(0)
        kwargs.update(dict(hosts=alive))
        return self.async_call(first, *args, **kwargs)

    def call_in_cluster(self,cluster_id,*args,**kwargs):
        '''
        在集群中选择一台活着的节点做同步调用
        '''
        alive = self.alive_hosts(cluster_id)
        if alive:
            return self.call(alive[0],*args,**kwargs)

    def call(self, host=None, *args, **kwargs):
        result = self.async_call(host, *args, **kwargs)
        return result, result.get()

    def __call__(self, *args, **kwargs):
        return self.task.run(*args,**kwargs)

class link_sync_call(pingMixin,commitMixin):
    '''
    将task变为链式调用，同步返回,指定hosts参数，调用从第一个host开始
    @link_sync_call
    @task()
    def test_transcation(a):
        ...
    '''

    def __init__(self, task):
        self.task = task
        self.run = self.task.run
        self.task.run = self
        self.hosts = []
        # self.uuid = uuid.uuid4()

    def __call__(self, *args, **kwargs):
        if not kwargs.has_key('uuid'):
            kwargs['uuid'] = uuid.uuid4().hex
        if kwargs.has_key('hosts'):
            self.hosts = kwargs['hosts']
            #本地调用不需要hosts参数
            kwargs.pop('hosts')
        else:
            LOG.info('execute task at last node')

            # func = inspect.getargs(self.run.func_code)
            # if 'last' in func.args:
            #     kwargs['last'] = True
        #本地调用
        result = self.run(*args, **kwargs)
        if self.hosts and self.hosts[0]:
            if not self.service_is_alive(self.hosts[0]):
                raise exception.ServiceNotFound(self.hosts[0])
            host = self.hosts.pop(0)
            kwargs['hosts'] = self.hosts
            self.task.apply_async(queue=host, args=args, kwargs=kwargs).get()
        return result

    def apply_async(self, *args, **kwargs):
        return self.task.apply_async(*args, **kwargs)



#注意：初始化顺序一定是 存储，网络，vm
from cvirt.tasks.storage import *
from cvirt.tasks.network import *
from cvirt.tasks.vm import *

clazz = pingMixin()

# @TaskWrap
# @task()
# def test_transaction():
#     from cvirt.manager.models import Network
#     i = Network.objects.first()
#     print type(i)
#     return [1,2,3,4]
#
# from celery import task
#


@task()
def schedule_task_broker(**kwargs):
    from cvirt.tasks.schedule import ScheduleDispatcher
    dispatcher = ScheduleDispatcher(kwargs)
    fun = getattr(dispatcher, kwargs['task_name'].replace('_', ''))
    fun()